Solar battery autoswitch


 


This circuit is based around the PIC 16C84 (or PIC 16F84) microcontroller. This chip is actually a small computer contained in a single chip, including RAM memory, EEPROM, I/O ports, CPU and so on. When you buy this chip, it comes empty with no program on it. You have to compile the source code and download the resulting machine code into it, using a PC and a small programmer attached to the parallel port of the PC and the chip. To get yourself familiar with this stuff, I suggest you first read this link: Getting started with microcontrollers.


A solar battery autoswitch is ment for use in campers. The switch establishes a "bridge" between the two batteries (comfortbattery and startbattery). This bridge can be either "on" or "off" (the circuit will do this automatically). If the bridge is "on", the two batteries are connected in parallel.
The circuit is exclusively for use in combination with a solar array (and a suited solar charger). The solar array normally only charges the comfortbattery. The idea of this circuit is, when the comfortbattery is almost completely charged, to use the "excessive" solar energy to also load the startbattery and keep it charged. This way it is possible, for instance, to leave the camper unused for a longer time and yet be able to start the engine without any problems, even if a lot of standby equipment (radio, clock, ...) is connected to the startbattery.
Another function of the circuit is to use the startbattery also as a comfortbattery so that more energy is available. Caution: this is only possible if the startbattery is suitable for both starting and for living, the so-called semi-traction batteries. The circuit takes care that this is only done if both batteries are charged at a reasonable level. The bridge will then be put to "on" and both batteries are used simultaneously for, by example, watching tv. When the voltage drops to a certain level, the bridge will be put to the "off" position (and from that moment on you are watching tv only on the comfortbattery). This is to garantee that the startbattery always has enough energy to start the engine.
The circuit is not suited to couple the two batteries while riding (this is because of the limited current the used relay is suited for), for this you have the use a separate classical relay or diode-bridge. When the car contact is on, the circuit will switch the bridge to the "off" position.



Circuit explanation:

The power supply for the circuit is taken from the start battery, using a 78L05 voltage regulator.

The voltage measured is that from the comfortbattery or the combination comfort/start battery (bridge on). The voltage is measured with the aid of a voltage divider constructed of R3/R4 to stay within a 5V limit. The maximum input voltage is 15V (enough for the situation where the battery is fully charged and the sun shines).

When the voltage drops below 12,4 volts, the brigde will be put off. When the voltage rises above 13,5 volts, the bridge will be put on. Between these two values the bridge state is left as-is (which can be either on or off, depending from where we are coming from).
The operation of the circuit depends on the use of 2 opamps which compare the voltage against a preset voltage. One opamp is used for the "low" treshold, the other one is used for the "high" treshold. The result will always be 0 or 1 (below or above treshold) and the two outputs from the opamps are connected to the input of a microcontroller which will handle the control of the bridge programmatically.
The two opamps can be found in IC LM324 which has four of them, so that should be enough.

The low treshold and the high treshold should be set at around 12,4 and 13,5 volts resp. Adjustment can occur via POT1 and POT2.

The bridge itself consists of a so-called bi-stable relay which has two coils (one for off and one for on). This relay has a coil voltage of 12V and a switching current of 12A at 12V.
The switching contacts of the bridge are not shown on the schematic diagram, you just should use the relays make-contact and connect one end of it with the start battery, the other end goes to the comfort battery.
A bi-stable relay does not use any current no matter in which position it is (bridge on or bridge off). Only during changing states you need a current for a short while (about 0,2 seconds pulse).
Both coils of the bi-stable relay are powered using a BC337 transistor, which as capable of doing this job. Notice that the coils are in the 12V circuit, not in the 5V circuit.
Relay 3 in the schematic diagram is the "off" coil, Relay 2 is the "on" coil. Relay 1, which is not strictly necessary, is used to disable an external alarm system while switching, for a short while. This is done only because I have an alarm system that is triggered by sudden voltage changes. You can do without Relay 1 if you don't need such an interface with your alarm system.

You can also operate the bridge manually using pushbutton SW1. This works only when the voltage is in the floating window, that is between 12,4 and 13,5 volts.

Like mentioned before, the relay is a bi-stable relay. You should use a heavy model, for instance Conrad ordering nbr. 504173 - 62. This one has a switching capability of 16 amps. The software takes care of the pulse operation. If you can find a bi-stable relay that has an even higher switching capability of say 30 amps (which I couldn't), you might use the circuit for making the bridge while driving. In that case however, a software adjustment is necessary.

Two LEDs (with current limiting resistor) can come in handy when connected to both coils of the bi-stable relay (this is not shown in the diagram). Then you can see when bridging/de-bridging occurs (LED whill shortly light).


Below the block diagram which shows how to connect the solar autoswitch in relation to the existing wiring in the camper.
The red lines show the new wiring.


Block diagram in relation to the rest of the camper.


Source code:

;;



;; 
;;    Zonnebatterij automaat, a PIC 16F84 program to switch two lead-acid batteries in parallel
;;    when there is enough sunlight.
;;    
;;    Copyright (C) 2000  Geert Van Espen (geert@vanespen.com)
;;
;;
        LIST P=16F84

        errorlevel 0,-305

        
        processor 16f84
        include "p16f84.inc"

 __CONFIG _CP_OFF & _RC_OSC & _PWRTE_ON  & _WDT_ON


;
;
;
;
;     commando om dit project naar de pic te sturen:
;       TOPIC -RWG SOLARSW.HEX
;
;
;
;
;
;      current settings: low level  = 12.29 V
;                        high level = 13.45 V
;




                                ; general purpose register allocation


werk    equ     0x0c            ; werkregister
t1      equ     0x0d            ; telwerk 1
t2      equ     0x0e            ; telwerk 2
        
TIJDl   equ     0x10            ; to keep track of time
TIJDh   equ     0x11

ddd     equ     0x13            ; debug only

KLAPPERTIJD0 equ 0x14           ; hysteresis 0
KLAPPERTIJD1 equ 0x15           ; hysteresis 1
KLAPPERTIJD2 equ 0x16           ; hysteresis 2
KLAPPERTIJD3 equ 0x17           ; hysteresis 3

RB0_STAND equ   0x18            ; 0=uit 1=aan
RB1_STAND equ   0x19            ; 0=uit 1=aan
RB2_STAND equ   0x1a            ; 0=uit 1=aan
RB3_STAND equ   0x1b            ; 0=uit 1=aan

tempvar equ     0x1c            ;

Wl      equ     0x20            ;
Wh      equ     0x21            ;
BRUG    equ     0x22            ; 0 = geen doorkoppeling batterijen
                                ; 1 = wel doorkoppeling batterijen
CONTACT equ     0x23            ; 0 = contactsleutel uit
                                ; 1 = contactsleutel aan
PrevRB0 equ     0x24            ; vorige RB0 situatie
PrevRB1 equ     0x25            ; vorige RB1 situatie
PrevRB2 equ     0x26            ; vorige contact situatie
PrevBtn equ     0x27            ; vorige pushbutton situatie

; tijdconstanten
;;;;;;;;;;;;;;;;

m_input_a equ   b'00000000'     ; input mask, which bits are input
m_input_b equ   b'11111111'     ;

        org     0x00            ; reset vector
        goto    main

        dt      "2000-10-10 geert@vanespen.com Zonnebatterij automaat"






;***********************************************************************
;*                                                                     *
;*     initialise                                                      *
;*                                                                     *
;***********************************************************************

initialise:                     ; set up ports

        MOVLW    0              ;
        TRIS     5              ;
        MOVLW    0x0F           ;
        TRIS     6              ;
        MOVLW    0x80           ;
        OPTION                  ;


        clrf    TIJDl           ; initialisaties
        clrf    TIJDh           ;
        clrf    KLAPPERTIJD0    ;
        clrf    KLAPPERTIJD1    ;
        clrf    KLAPPERTIJD2    ;
        clrf    KLAPPERTIJD3    ;
        clrf    RB0_STAND       ;
        clrf    RB1_STAND       ;
        clrf    RB2_STAND       ;
        clrf    RB3_STAND       ;
        clrf    BRUG            ;
        clrf    CONTACT         ;
        clrf    PrevRB0         ;
        clrf    PrevRB1         ;
        clrf    PrevRB2         ;
        clrf    PrevBtn         ;

        clrf    PORTA           ; UIT laag
        clrf    PORTB


; uitgang even aan en uit laten knipperen om te laten zien dat het
; apparaat werkt
        movlw   0xFE            ; startwaarde
        movwf   PORTA           ;

        movlw   6               ; knipper 3 keer
        movwf   t1              ;
functiontest:
        movlw   0x19            ; wacht halve seconde
        call    wachtlus        ;
        movlw   0xFF            ;
        xorwf   PORTA, f        ;
        decfsz  t1, f           ;
        goto    functiontest    ;
        
        clrf    PORTA           ; UIT laag

        retlw   0


;***********************************************************************
;*                                                                     *
;*     wachtlus                                                        *
;*                                                                     *
;***********************************************************************
wachtlus:
; wacht x keer 18 milliseconden, met x in w

        movwf   werk            ; zet w in werk
; debug only
; movlw 1
; movwf werk

slapertje:
        sleep                   ;
        decfsz  werk, f         ;
        goto    slapertje       ;
        retlw   0               ;




;***********************************************************************
;*                                                                     *
;*     test_portRB0                                                    *
;*                                                                     *
;***********************************************************************
; hysteresis voor poort RB0: als RB0 een tijdje 0 is wordt RB0_STAND
; ook 0; als RB0 1 is wordt RB0_STAND onmiddellijk 1
test_portRB0:
        movf    PORTB, w        ; w = PORTB
        andlw   1               ; we willen alleen RB0
        movwf   tempvar         ;
        movf    tempvar, w      ;
        btfss   tempvar, 0      ;
        goto    test0_low       ;
        movlw   1               ;
        movwf   RB0_STAND       ;
        clrf    KLAPPERTIJD0    ; KLAPPERTIJD0 = 0
        retlw   0               ;
test0_low:
        subwf   RB0_STAND, w    ;
        btfss   STATUS, Z       ; RB0 input = RB0_STAND?
        goto    test0_hyst1     ;
        clrf    KLAPPERTIJD0    ; ja: KLAPPERTIJD0 = 0
        goto    test0_hyst2     ;
test0_hyst1:
        incf    KLAPPERTIJD0, f ; neen: verhoog KLAPPERTIJD0
test0_hyst2:
        btfss   KLAPPERTIJD0, 2 ; KLAPPERTIJD0 hoog?
        goto    test0_hyst3     ; neen: gedaan
        movf    tempvar, w      ;
        movwf   RB0_STAND       ; ja: RB0_STAND = RB0 input
        clrf    KLAPPERTIJD0    ; KLAPPERTIJD0 = 0
test0_hyst3:

        retlw   0               ;




;***********************************************************************
;*                                                                     *
;*     test_portRB1                                                    *
;*                                                                     *
;***********************************************************************
; hysteresis voor poort RB1: als RB1 een tijdje 0 is wordt RB1_STAND
; ook 0; als RB1 1 is wordt RB1_STAND onmiddellijk 1
test_portRB1:
        movf    PORTB, w        ; w = PORTB
        andlw   2               ; we willen alleen RB1
        movwf   tempvar         ;
        bcf     STATUS, C       ;
        rrf     tempvar, f      ;
        movf    tempvar, w      ;
        btfss   tempvar, 0      ;
        goto    test1_low       ;
        movlw   1               ;
        movwf   RB1_STAND       ;
        clrf    KLAPPERTIJD1    ; KLAPPERTIJD1 = 0
        retlw   0               ;
test1_low:
        subwf   RB1_STAND, w    ;
        btfss   STATUS, Z       ; RB1 input = RB1_STAND?
        goto    test1_hyst1     ;
        clrf    KLAPPERTIJD1    ; ja: KLAPPERTIJD1 = 0
        goto    test1_hyst2     ;
test1_hyst1:
        incf    KLAPPERTIJD1, f ; neen: verhoog KLAPPERTIJD1
test1_hyst2:
        btfss   KLAPPERTIJD1, 2 ; KLAPPERTIJD1 hoog?
        goto    test1_hyst3     ; neen: gedaan
        movf    tempvar, w      ;
        movwf   RB1_STAND       ; ja: RB1_STAND = RB1 input
        clrf    KLAPPERTIJD1    ; KLAPPERTIJD1 = 0
test1_hyst3:

        retlw   0               ;




;***********************************************************************
;*                                                                     *
;*     test_portRB2                                                    *
;*                                                                     *
;***********************************************************************
; hysteresis voor poort RB2: als RB2 een tijdje 1 is wordt RB2_STAND
; ook 1; als RB2 een tijdje nul is wordt RB2_STAND ook nul
test_portRB2:
        movf    PORTB, w        ; w = PORTB
        andlw   0x04            ; we willen alleen RB2
        movwf   tempvar         ;
        bcf     STATUS, C       ;
        rrf     tempvar, f      ;
        rrf     tempvar, f      ;
        movf    tempvar, w      ;
        subwf   RB2_STAND, w    ;
        btfss   STATUS, Z       ; RB2 input = RB2_STAND?
        goto    test2_hyst1     ;
        clrf    KLAPPERTIJD2    ; ja: KLAPPERTIJD2 = 0
        goto    test2_hyst2     ;
test2_hyst1:
        incf    KLAPPERTIJD2, f ; neen: verhoog KLAPPERTIJD2
test2_hyst2:
        btfss   KLAPPERTIJD2, 1 ; KLAPPERTIJD2 hoog?
        goto    test2_hyst3     ; neen: gedaan
        movf    tempvar, w      ;
        movwf   RB2_STAND       ; ja: RB2_STAND = RB2 input
        clrf    KLAPPERTIJD2    ; KLAPPERTIJD2 = 0
test2_hyst3:

        retlw   0               ;


;***********************************************************************
;*                                                                     *
;*     test_portRB3                                                    *
;*                                                                     *
;***********************************************************************
; hysteresis voor poort RB3: als RB3 een tijdje 1 is wordt RB3_STAND
; ook 1; als RB3 een tijdje nul is wordt RB3_STAND ook nul
test_portRB3:
        movf    PORTB, w        ; w = PORTB
        andlw   0x08            ; we willen alleen RB3
        movwf   tempvar         ;
        bcf     STATUS, C       ;
        rrf     tempvar, f      ;
        rrf     tempvar, f      ;
        rrf     tempvar, f      ;
        movf    tempvar, w      ;
        subwf   RB3_STAND, w    ;
        btfss   STATUS, Z       ; RB3 input = RB3_STAND?
        goto    test3_hyst1     ;
        clrf    KLAPPERTIJD3    ; ja: KLAPPERTIJD3 = 0
        goto    test3_hyst2     ;
test3_hyst1:
        incf    KLAPPERTIJD3, f ; neen: verhoog KLAPPERTIJD3
test3_hyst2:
        btfss   KLAPPERTIJD3, 2 ; KLAPPERTIJD3 hoog?
        goto    test3_hyst3     ; neen: gedaan
        movf    tempvar, w      ;
        movwf   RB3_STAND       ; ja: RB3_STAND = RB3 input
        clrf    KLAPPERTIJD3    ; KLAPPERTIJD3 = 0
test3_hyst3:

        retlw   0               ;



;***********************************************************************




;***********************************************************************
;*                                                                     *
;*     programma                                                       *
;*                                                                     *
;***********************************************************************



main:
        btfsc   STATUS, NOT_TO  ; if this is a reset or power-up ...
        call    initialise      ; set port drivers and WDT prescaler





        movlw   1               ; veronderstel contact aan
        movwf   CONTACT         ;

        movlw   3               ;
        movwf   PrevRB0         ;
        movwf   PrevRB1         ;
        movwf   PrevRB2         ;
        movwf   PrevBtn         ;

start:

main_loop:

;
; LAGE DREMPELSPANNING (poort RB0)
;
        call    test_portRB0    ; test spanning op poort RB0
        movf    PrevRB0, w      ; test of RB0_STAND veranderd is
        subwf   RB0_STAND, w    ;
;;;;;   btfsc   STATUS, Z       ; is de stand veranderd?
;;;;;   goto    test0_af        ;
; veranderd
        movf    RB0_STAND, w    ; ja, de stand is veranderd
        movwf   PrevRB0         ;

        btfsc   RB0_STAND, 0    ; is spanning laag?
        goto    test0_af        ; neen, gedaan
        btfss   BRUG, 0         ; ja: test of brug al af stond
        goto    test0_af        ; brug af: gedaan
        bsf     PORTA, 2        ; spanning laag: alarm-disable relais
        bsf     PORTA, 0        ;   en RA0 een tijdje aanzetten

        movlw   0xc             ; wacht fractie seconde
        call    wachtlus        ;

        clrf    BRUG            ; bewaar huidige toestand

        bcf     PORTA, 0        ; poorten terug uitzetten
        bcf     PORTA, 2        ;

        movlw   0x6f            ; wacht 2 seconden
        call    wachtlus        ;
test0_af:


;
; HOGE DREMPELSPANNING (poort RB1)
;
        btfsc   CONTACT, 0      ; als contact aan,
        goto    test1_af        ; dan nooit brug maken!

        call    test_portRB1    ; test spanning op poort RB1
        movf    PrevRB1, w      ;
        subwf   RB1_STAND, w    ;
;;;;;   btfsc   STATUS, Z       ; is de stand veranderd?
;;;;;   goto    test1_af        ;
; veranderd
        movf    RB1_STAND, w    ; ja, de stand is veranderd
        movwf   PrevRB1         ;

        btfss   RB1_STAND, 0    ; is spanning hoog?
        goto    test1_af        ; neen, gedaan
        btfsc   BRUG, 0         ; ja: test of brug al aan stond
        goto    test1_af        ; brug aan: gedaan
        bsf     PORTA, 2        ; spanning hoog: alarm-disable relais
        bsf     PORTA, 1        ;   en RA1 een tijdje aanzetten

        movlw   0xc             ; wacht fractie seconde
        call    wachtlus        ;

        bsf     BRUG, 0         ; bewaar huidige toestand

        bcf     PORTA, 1        ; poorten terug uitzetten
        bcf     PORTA, 2        ;

        movlw   0x6f            ; wacht 2 seconden
        call    wachtlus        ;
test1_af:


;
; CONTACTSLEUTEL (poort RB2)
;

ContactSleutel:

        call    test_portRB2    ; test spanning op poort RB2
        movf    PrevRB2, w      ;
        subwf   RB2_STAND, w    ;
        btfsc   STATUS, Z       ; is de stand veranderd?
        goto    test2_af        ;
; veranderd
        movf    RB2_STAND, w    ; ja, de stand is veranderd
        movwf   PrevRB2         ;

        btfsc   RB2_STAND, 0    ; is contact aan?
        goto    test2_aan       ;
        clrf    CONTACT         ; neen: bewaar huidige toestand
        bsf     PrevRB0, 3      ; herinitialiseer spanningstesten
        bsf     PrevRB1, 3      ;
        goto    test2_af        ;
test2_aan:
        bsf     CONTACT, 0      ; bewaar huidige toestand

        bsf     PORTA, 2        ; alarm-disable relais en
        bsf     PORTA, 0        ;   RA0 een tijdje aanzetten

        movlw   0xc             ; wacht fractie seconde
        call    wachtlus        ;

        clrf    BRUG            ; bewaar huidige toestand

        bcf     PORTA, 0        ; poorten terug uitzetten
        bcf     PORTA, 2        ;

        movlw   0xfe            ; wacht 5 seconden
        call    wachtlus        ;


test2_af:


;
; PUSHBUTTON (poort RB3)
;
        btfss   CONTACT, 0      ; als contact aan,
        goto    PushButton      ; dan nooit brug maken!
        clrf    PrevBtn         ;
        goto    PushButton_samen;

PushButton:
        call    test_portRB3    ; als pushbutton gedrukt is,
        movf    PrevBtn, w      ;   dan toggelen
        subwf   RB3_STAND, w    ;
        btfsc   STATUS, Z       ; is de stand veranderd?
        goto    PushButton_gelijk;
PushButton_changed:
        movf    RB3_STAND, w    ; ja: PrevBtn = RB3_STAND
        movwf   PrevBtn         ;

        btfss   RB3_STAND, 0    ; is pushbutton gedrukt?
        goto    PushButton_af   ;
        movlw   1               ; ja: toggle BRUG
        xorwf   BRUG, f         ;

; reflect BRUG value onto output ports
        btfss   BRUG, 0         ; BRUG waarde testen
        goto    PushButton_Reflect_uit;
PushButton_Reflect_aan:
        bsf     PORTA, 2        ; alarm-disable relais en
        bsf     PORTA, 1        ;   RA1 een tijdje aanzetten
        movlw   0xc             ; wacht fractie seconde
        call    wachtlus        ;
        bcf     PORTA, 1        ; poorten terug uitzetten
        bcf     PORTA, 2        ;
        goto    PushButton_samen;
PushButton_Reflect_uit:
        bsf     PORTA, 2        ; alarm-disable relais en
        bsf     PORTA, 0        ;   RA0 een tijdje aanzetten
        movlw   0xc             ; wacht fractie seconde
        call    wachtlus        ;
        bcf     PORTA, 0        ; poorten terug uitzetten
        bcf     PORTA, 2        ;
        goto    PushButton_samen;
PushButton_samen:


PushButton_gelijk:
        goto    PushButton_af   ;


PushButton_af:





        movlw   0x2             ; wacht fractie seconde
        call    wachtlus        ;

        goto    start           ;

        ;; 
        ;;
        
        end





Download source code (assembler)       Download hex file (machine code)



The finished device.

  Home         Back to Electronic Projects